
#include "cooperative-condition.h"

#include <assert.h>
#include "fiber-context.h"

namespace dulymoo {

CooperativeCondition::CooperativeCondition(CooperativeMutex &mutex) :
    mutex_(mutex),
    waiting_queue_()
{
}

CooperativeCondition::~CooperativeCondition()
{
}

bool CooperativeCondition::Acquire()
{
    return mutex_.Lock();
}

bool CooperativeCondition::TryAcquire()
{
    return mutex_.TryLock();
}

void CooperativeCondition::Release()
{
    mutex_.Unlock();
}

bool CooperativeCondition::Wait()
{
    FiberEnv::ThreadContext *thread_context = FiberEnv::GetThreadContext();
    assert (thread_context != NULL);
    FiberContext *fiber_context = thread_context->GetCurrentFiber();
    assert (fiber_context != NULL);

    // it's assumed that the mutex is locked by the current fiber when entering Wait
    assert (mutex_.locked_ > 0);
    assert (mutex_.owner_ == fiber_context);

    fiber_context->SetState("condition.wait");
    waiting_queue_.push_back(fiber_context);

    // CompleteWait will move current fiber to the waiting queue of this
    // condition, and call mutex_.Unlock
    thread_context->Schedule(CompleteWait, reinterpret_cast<void*>(this));

    // when this fiber is re-activated, we should re-acquire the lock
    mutex_.Lock();
    return true;
}

void CooperativeCondition::CompleteWait(FiberEnv::ThreadContext* thread_context, void* arg)
{
    CooperativeCondition* cond = reinterpret_cast<CooperativeCondition*>(arg);
    assert (cond != NULL);

    cond->waiting_queue_.push_back(thread_context->GetCurrentFiber());
    cond->mutex_.Unlock();
}

bool CooperativeCondition::TimedWait(const ABSTime &time)
{
    FiberEnv::ThreadContext *thread_context = FiberEnv::GetThreadContext();
    assert (thread_context != NULL);
    FiberContext *fiber_context = thread_context->GetCurrentFiber();
    assert (fiber_context != NULL);

    // it's assumed that the mutex is locked by the current fiber when entering TimedWait
    assert (mutex_.locked_ > 0);
    assert (mutex_.owner_ == fiber_context);

    // TODO:
    return false;
}

bool CooperativeCondition::TimedWait(unsigned int timeout)
{
    ABSTime time;
    time.Adjust(timeout * 1000);
    return TimedWait(time);
}

bool CooperativeCondition::Signal()
{
    FiberEnv::ThreadContext *thread_context = FiberEnv::GetThreadContext();
    assert (thread_context != NULL);
    FiberContext *fiber_context = thread_context->GetCurrentFiber();
    assert (fiber_context != NULL);

    // it's assumed that the mutex is locked by the current fiber when entering Signal
    assert (mutex_.locked_ > 0);
    assert (mutex_.owner_ == fiber_context);

    if (!waiting_queue_.empty())
    {
        FiberContext* waiting_fiber = waiting_queue_.front();
        waiting_queue_.pop_front();

        thread_context->ReadyFiber(waiting_fiber);
    }

    return true;
}

bool CooperativeCondition::Broadcast()
{
    FiberEnv::ThreadContext *thread_context = FiberEnv::GetThreadContext();
    assert (thread_context != NULL);
    FiberContext *fiber_context = thread_context->GetCurrentFiber();
    assert (fiber_context != NULL);

    // it's assumed that the mutex is locked by the current fiber when entering Broadcast
    assert (mutex_.locked_ > 0);
    assert (mutex_.owner_ == fiber_context);

    while (!waiting_queue_.empty())
    {
        FiberContext* waiting_fiber = waiting_queue_.front();
        waiting_queue_.pop_front();

        thread_context->ReadyFiber(waiting_fiber);
    }

    return true;
}

}  // namespace dulymoo

